package com.ZeroCarbon.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.ZeroCarbon.client.UserClient;
import com.ZeroCarbon.domain.dto.UserLoginDTO;
import com.ZeroCarbon.domain.po.Account;
import com.ZeroCarbon.entity.Result;
import com.ZeroCarbon.exception.BusinessLogicException;
import com.ZeroCarbon.mapper.AccountMapper;
import com.ZeroCarbon.service.IAccountService;
import com.ZeroCarbon.utils.EmailService;
import com.ZeroCarbon.utils.JwtTool;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

import static com.ZeroCarbon.exception.BusinessLogicException.BUSY_EXCEPTION;
import static com.ZeroCarbon.exception.BusinessLogicException.ERROR_EXCEPTION;
import static com.ZeroCarbon.prefix.RedisKey.LOGIN;
import static com.ZeroCarbon.prefix.RedisKey.STRING;
import static com.ZeroCarbon.utils.BusinessMethodUtils.isExist;

/**
 * 登录实现类
 *
 * @since 2024-10-8
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements IAccountService {

    private final StringRedisTemplate redis;

    private final UserClient userClient;

    /**
     * Jwt工具
     */
    private final JwtTool jwtTool;

    /**
     * 邮箱信息类
     */
    private final EmailService emailService;

    /**
     * 保存账号
     *
     * @param account 账号信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveAccount(Account account) throws BusinessLogicException {
        //标记
        boolean flag = true;
        //获取注册账号
        String id = account.getId();
        //查看账号是否已经被注册
        if (isExist(STRING, id)) {
            flag = false;
        }
        try {
            //将账号添加到缓存
            redis.opsForSet().add(STRING, id);
            //保存账号到数据库
            flag = flag && save(account);
        } catch (Exception e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
        //注册失败
        if (!flag) {
            throw new BusinessLogicException("账号已被注册，请更换账号重新注册");
        }
    }

    /**
     * 更新账号
     *
     * @param account 账号信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAccount(Account account) throws BusinessLogicException {
        //判断账号是否被注册
        boolean flag = isExist(STRING, account.getId());
        try {
            //更新账号信息
            flag = flag && updateById(account);
        } catch (Exception e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
        //更新账号失败
        if (!flag) {
            throw new BusinessLogicException("更新账号失败");
        }
    }

    /**
     * 登录
     *
     * @param account 账号信息
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public UserLoginDTO login(Account account, HttpServletRequest request) throws BusinessLogicException {
        //获取账号
        String id = account.getId();
        //获取密码
        String password = account.getPassword();
        //账号构建器
        LambdaQueryWrapper<Account> accountWrapper = new LambdaQueryWrapper<Account>()
                .eq(Account::getId, id);
        //判断账户是否为空
        if (!exists(accountWrapper)) {
            throw new BusinessLogicException("账号未注册，请先注册账号");
        }
        //账户构建器
        LambdaQueryWrapper<Account> accountSureWrapper = new LambdaQueryWrapper<Account>()
                .eq(Account::getId, id)
                .eq(Account::getPassword, password);
        //判断账号密码是否正确
        if (!exists(accountSureWrapper)) {
            throw new BusinessLogicException("输入密码错误，请重新输入");
        }
        //生成Access Token，半小时有效
        String accessToken = jwtTool.generateAccessToken(id, request, Duration.ofMinutes(30));
        //生成Refresh Token，三天有效
        String refreshToken = jwtTool.generateRefreshToken(id,
                request.getHeader("X-Real-IP"),
                request.getHeader("User-Agent"),
                Duration.ofDays(3));
        //记录结果
        Result result = null;
        try {
            //远程调用返回结果
            result = userClient.getBaseInfo(id);
        } catch (Exception e) {
            throw new BusinessLogicException(BUSY_EXCEPTION);
        }
        //拷贝用户属性到UserLoginDTO
        UserLoginDTO userLoginDTO = BeanUtil.copyProperties(result.getData(), UserLoginDTO.class);
        //设置token信息
        userLoginDTO.setAccessToken(accessToken).setRefreshToken(refreshToken);
        //添加已登陆帐号到redis缓存，设置过期时间为30分钟
        String key = LOGIN + id;
        try {
            //将已登录的状态保存到缓存中
            redis.opsForValue().set(key, "1", 30, TimeUnit.MINUTES);
        } catch (Exception e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
        //返回对象
        return userLoginDTO;
    }

    /**
     * 获取验证码
     *
     * @param email 目的邮箱
     */
    @Override
    public void getVerificationCode(String email) throws BusinessLogicException {
        //生成6位随机验证码
        int code = (int) (Math.random() * 900000) + 100000;
        //验证码文本
        String text = "【零碳易行】您的验证码为：" + code + "，15分钟内有效。请勿泄露给他人，如非本人操作请忽略本短信。";
        try {
            //发送邮箱
            emailService.sendSimpleEmail(email, "零碳易行注册验证码", text);
            //将验证码保存到redis，并设置15分钟后过期
            redis.opsForValue().set(email, String.valueOf(code), 15, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.error(e.toString());
            throw new BusinessLogicException("邮箱不存在");
        }
    }

    /**
     * 校验验证码
     */
    @Override
    public void verifyVerificationCode(String email, String code) throws BusinessLogicException {
        //从redis获取该邮箱的验证码
        String generateVerificationCode = redis.opsForValue().get(email);
        //判断生成验证码是否存在
        if (generateVerificationCode == null) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
        //判断输入验证码是否与生成验证码相同
        if (!generateVerificationCode.equals(code)) {
            throw new BusinessLogicException("验证码错误");
        }
    }
}
